/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.admin.security.listener;

import vip.dexian.admin.entity.LoginLog;
import vip.dexian.admin.manager.AdminManager;
import vip.dexian.admin.service.LoginLogService;
import vip.dexian.common.utils.IpToLongUtils;
import vip.dexian.common.utils.UserAgentUtils;
import vip.dexian.common.utils.WebUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.authentication.event.AuthenticationFailureCredentialsExpiredEvent;
import org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent;
import org.springframework.security.authentication.event.AuthenticationFailureExpiredEvent;
import org.springframework.security.authentication.event.AuthenticationFailureLockedEvent;
import org.springframework.security.authentication.event.AuthenticationFailureProviderNotFoundEvent;
import org.springframework.security.authentication.event.AuthenticationFailureProxyUntrustedEvent;
import org.springframework.security.authentication.event.AuthenticationFailureServiceExceptionEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;

/**
 * 认证失败监听器
 *
 * @author 挺好的 2023年06月06日 16:14
 */
@Slf4j
@Component
public class AuthenticationFailureListener implements ApplicationListener <AbstractAuthenticationFailureEvent> {

    /**
     * 管理员登录日志
     */
    @Resource (name = "loginLogServiceImpl")
    private LoginLogService adminLoginLogService;

    /**
     * 管理员
     */
    @Resource (name = "adminManagerImpl")
    private AdminManager adminManager;


    @Override
    public void onApplicationEvent (AbstractAuthenticationFailureEvent event) {

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(
                RequestContextHolder.getRequestAttributes())).getRequest();

        String message;
        if (event instanceof AuthenticationFailureBadCredentialsEvent) {
            //提供的凭据是错误的，用户名或者密码错误
            message = "提供的凭据是错误的，用户名或者密码错误";

        } else if (event instanceof AuthenticationFailureCredentialsExpiredEvent) {
            //验证通过，但是密码过期
            message = "验证通过，但是密码过期";
        } else if (event instanceof AuthenticationFailureDisabledEvent) {
            //验证过了但是账户被禁用
            message = "验证过了但是账户被禁用";
        } else if (event instanceof AuthenticationFailureExpiredEvent) {
            //验证通过了，但是账号已经过期
            message = "验证通过了，但是账号已经过期";
        } else if (event instanceof AuthenticationFailureLockedEvent) {
            //账户被锁定
            message = "账户被锁定";
        } else if (event instanceof AuthenticationFailureProviderNotFoundEvent) {
            //配置错误，没有合适的AuthenticationProvider来处理登录验证
            message = "配置错误";
        } else if (event instanceof AuthenticationFailureProxyUntrustedEvent) {
            // 代理不受信任，用于Oauth、CAS这类三方验证的情形，多属于配置错误
            message = "代理不受信任";
        } else if (event instanceof AuthenticationFailureServiceExceptionEvent) {
            // 其他任何在AuthenticationManager中内部发生的异常都会被封装成此类
            message = "内部发生的异常";
        } else {
            message = "其他未知错误";
        }

        // 登录账号
        Object username = event.getAuthentication().getPrincipal();
        // 登录密码
        Object credentials = event.getAuthentication().getCredentials();

        // 登录失败
        this.adminManager.loginFailure(username.toString());

        UserAgentUtils.UserAgentInfo userAgentInfo = UserAgentUtils.parse(request);

        LoginLog loginLog = LoginLog.builder().username(username.toString()).password(credentials.toString())
                .isSuccess(false).memo(message).ip(IpToLongUtils.toLong(WebUtils.getIPAddr(request)))
                .browser(userAgentInfo.getBrowser()).browserVersion(userAgentInfo.getBrowserVersion())
                .osName(userAgentInfo.getOsName()).deviceType(userAgentInfo.getDeviceType()).build();

        // 记录登录日志
        this.adminLoginLogService.login(loginLog);


    }
}
